/*****************************************************************//**
 * \file   AnsysAPDLReader_EBLOCK.cpp
 * \brief  AnsysAPDLReader_EBLOCK
 *
 * \author Wyatt
 * \date   May 2023
 *********************************************************************/
#include "AnsysAPDLReader_EBLOCK.h"
#include "CDBReader.h"
#include "Includes.h"

#include "AnsysAPDLReader_Select.h"

IMPLEMENT_CDB_READER(AnsysAPDLReader_EBLOCK, KEY_EBLOCK, "EBLOCK")
IMPLEMENT_CDB_READER(AnsysAPDLReader_TYPE, KEY_TYPE, "TYPE")
IMPLEMENT_CDB_READER(AnsysAPDLReader_MAT, KEY_MAT, "MAT")
IMPLEMENT_CDB_READER(AnsysAPDLReader_REAL, KEY_REAL, "REAL")
IMPLEMENT_CDB_READER(AnsysAPDLReader_TSHAPE, KEY_TSHAPE, "TSHAPE")
IMPLEMENT_CDB_READER(AnsysAPDLReader_EN, KEY_EN, "EN")

AnsysAPDLReader_EBLOCK::AnsysAPDLReader_EBLOCK(CDBReader* pCDBReader)
	: ReaderBase(pCDBReader)
{
}

bool AnsysAPDLReader_EBLOCK::Scan(std::string& strDataLine)
{
	if (_inBlock == IN_NONE) {
		return ScanHead(strDataLine);
	}
	return ScanData(strDataLine);
}

bool AnsysAPDLReader_EBLOCK::ScanHead(std::string& strDataLine)
{
	_nNodesNum = 0;
	_nLine = 1;
	std::vector<std::string> vecData;
	int nparam = split(strDataLine, vecData);
	if (nparam < 3)
		return false;
	if (strDataLine.find_first_of("SOLID") != 0)
		_bSolid = true;
	else
		_bSolid = false;
	_nNodesNum = std::stol(vecData[1]);
	_elemCnt = 0;
	_inBlock = IN_YES;
	return true;
}

bool AnsysAPDLReader_EBLOCK::ScanData(std::string& strDataLine)
{
	if (strDataLine.size() == 0)
		return true;
	if (strDataLine.compare("-1") >= 0)
	{
		_nLine++;
		_inBlock = IN_NONE;
		return true;
	}
	if (strDataLine[0] == '(' || _nLine == 1)
	{
		_nLine++;
		return true;
	}
	std::vector<std::string> vecData;
	int n = splitView(strDataLine, vecData, " ", true, true);
	if (_bSolid)
	{
		if (!_bNextLine)
		{
			if (vecData.size() > 8)
			{
				int ncnt = 0;
				if (!string2Integer(vecData[8], ncnt));
				if (ncnt > 8)
					_bNextLine = true;
			}
		} else {
			_bNextLine = false;
		}
	}
	if (!_bNextLine)
	{
		_elemCnt++;
	}
	return true;
}

bool AnsysAPDLReader_EBLOCK::Read(std::string& strDataLine)
{
	if (_inBlock == IN_NONE) {
		return ReadHead(strDataLine);
	}
	return ReadData(strDataLine);
}

bool AnsysAPDLReader_EBLOCK::ReadHead(std::string& strDataLine)
{
	_elementIdRead.clear();

	_nNodesNum = 0;
	_nLine = 1;
	_bSolid = false;
	_bNextLine = false;

	std::vector<std::string> vecData;
	int n = split(strDataLine, vecData);
	if (n < 2)
		return false;
	for (int i = 0; i < n; i++)
	{
		std::string& str = vecData[i];
		switch (i)
		{
		case 1:
			if (!string2Integer(str, _nNodesNum))
				return false;
			break;
		case 2:
			toUpper(str);
			if (str == "SOLID")
				_bSolid = true;
			break;
		}
	}

	if (!_bSolid)
	{ 
		_pCDBReader->ifEleToSet = true;
	}

	if (_pCDBReader->ifEleToSet)
	{
		_pCDBReader->_elementToSet.reserve(_elemCnt);
		_pCDBReader->_elemAnsysIdToSetid.reserve(_elemCnt);
	}
	else
	{
		_pCDBReader->GetElemVec().reserve(_pCDBReader->GetElemVec().size() + _elemCnt);
		_pCDBReader->GetElemIdxMap().reserve(_pCDBReader->GetElemIdxMap().size() + _elemCnt);
	}
	_inBlock = IN_YES;
	return true;
}

bool AnsysAPDLReader_EBLOCK::ReadData(std::string& strDataLine)
{
	if (strDataLine.size() == 0)
		return true;
	Trim(strDataLine);
	if (strcmp(strDataLine.c_str(), "-1") == 0)
	{
		AnsysAPDLReader_ESEL::_instance->_elementSelected.clear();
		AnsysAPDLReader_ESEL::_instance->_elementSelected.selType = ANSYS_TARGET_TYPE::TARGET_ELEM;
		AnsysAPDLReader_ESEL::_instance->_elementSelected.selectData = _elementIdRead;

		_nLine++;
		_inBlock = IN_NONE;
		return true;
	}
	if (strDataLine[0] == '(' || _nLine == 1)
	{
		_nLine++;
		return true;
	}
	std::vector<std::string> vecData;
	int n = splitView(strDataLine, vecData, " ", true, true);
	SKey nid = 0;
	SNum3d vPos;
	if (_bSolid)
	{		
		if (!_bNextLine)
		{
			std::vector<int> dataLine(n);
			for (int i = 0; i < n; i++)
			{
				if (i < 19)
				{
					if (!string2Integer(vecData[i], dataLine[i]))
						return false;
				}
				else return false;
			}
			if (dataLine[8] > 8)
				_bNextLine = true;

			_elem.matNum = dataLine[0];
			_elem.elementType = dataLine[1];
			_elem.realConstantNum = dataLine[2];
			_elem.sectinId = dataLine[3];
			_elem.elementCoorNum = dataLine[4];
			_elem.birthOrDeath = dataLine[5];
			_elem.solidReference = dataLine[6];
			_elem.elementShapeFlag = dataLine[7];
			_elem.nodesNumInEle = dataLine[8];
			_elem.ansysID = dataLine[10];
			_elem.nodeIds.resize(_elem.nodesNumInEle);

			int thisLineNodes = _elem.nodesNumInEle;
			if (_bNextLine)
			{
				thisLineNodes = 8;
			}
			for (size_t i = 0; i < thisLineNodes; i++)
			{
				if (_pCDBReader->ifHaveNode(dataLine[11 + i]))
				{
					_elem.nodeIds[i] = dataLine[11 + i];
				}
				else
				{
					//ReporterFatal<std::string> rep("Can't find node id of " + std::to_string(dataLine[11 + i]), _pCDBReader->GetLineNumber(), _pCDBReader->filePath(), REP_FATAL_CODE_INVALID_VALUE);
					throw ;
				}
			}

			//_elem.solidReference = _bSolid;
			//_elem.id = _elem.oldId = _elem.data[10];
			//_prop = std::make_tuple(_elem.data[0]
			//	, _elem.data[1]
			//	, _elem.data[2]
			//	, _elem.data[3]
			//);
		}
		else
		{
			int valueT = 0;
			for (size_t i = 0; i < n; i++)
			{				
				if (i < 12)
				{
					if (string2Integer(vecData[i], valueT))
					{
						_elem.nodeIds[8 + i] = valueT;
					}
					else
					{
						return false;
					}						
				}
				else
					return false;
			}
			_bNextLine = false;
		}
	}
	else
	{
		std::vector<int> dataLine(n);
		for (int i = 0; i < n; i++)
		{
			if (i < 14)
			{
				if (!string2Integer(vecData[i], dataLine[i]))
					return false;
			}
		}
		if (!_bNextLine)
		{
			_elem.ansysID = dataLine[0];
			_elem.matNum = dataLine[3];
			_elem.realConstantNum = dataLine[2];
			_elem.sectinId = dataLine[1];
			_elem.elementCoorNum = dataLine[4];
			_elem.solidReference = -1;
			_elem.elementType = dataLine[1];

			_elem.nodesNumInEle = n - 5;
			_elem.nodeIds.resize(_elem.nodesNumInEle);
			for (size_t i = 0; i < _elem.nodesNumInEle; i++)
			{
				_elem.nodeIds[i] = dataLine[i + 5];
			}
		}
		else
		{
			size_t sourceSize = _elem.nodeIds.size();
			_elem.nodeIds.resize(sourceSize + dataLine.size());
			for (size_t i = 0; i < dataLine.size(); i++)
			{
				_elem.nodeIds[sourceSize + i] = dataLine[i];
			}
		}
		
	}
	if (!_bNextLine)
	{
		if (_pCDBReader->ifEleToSet)
		{
			_elem.ifOutElment = false;
			_pCDBReader->AddElementToset(_elem);
		}
		else
		{
			_elem.ifOutElment = true;
			_pCDBReader->AddElement(_elem);
		}		
		_elementIdRead.emplace_back(_elem.ansysID);
		_elem.Init();
		_pCDBReader->ifBuildMap = false;
		//_prop = std::make_tuple(0, 0, 0, 0);
	}
	_nLine++;
	return true;
}

AnsysAPDLReader_TYPE::AnsysAPDLReader_TYPE(CDBReader* pCDBReader)
	: ReaderBase(pCDBReader)
{

}

bool AnsysAPDLReader_TYPE::Read(std::string& strDataLine)
{
	std::vector<std::string> vecData;
	int n = split(strDataLine, vecData);
	int eid = 0;
	for (int i = 1; i < n; i++)
	{
		std::string data = vecData[i];
		switch (i)
		{
		case 1:
			if (!string2Integer(data, eid))
			{
				if (_pCDBReader->_mulSet.find(data) != _pCDBReader->_mulSet.end())
				{
					if (!string2Integer(_pCDBReader->_mulSet[data], eid))
					{
						return false;
					}
				}
				else
				{
					return false;
				}
			}
			break;
		default:
			break;
		}
	}

	tempNodeElement.elementType = eid;
	return true;
}

AnsysAPDLReader_MAT::AnsysAPDLReader_MAT(CDBReader* pCDBReader)
	: ReaderBase(pCDBReader)
{

}

bool AnsysAPDLReader_MAT::Read(std::string& strDataLine)
{
	std::vector<std::string> vecData;
	int n = split(strDataLine, vecData);
	int eid = 0;
	for (int i = 1; i < n; i++)
	{
		std::string data = vecData[i];
		switch (i)
		{
		case 1:
			if (!string2Integer(data, eid))
			{
				if (_pCDBReader->_mulSet.find(data) != _pCDBReader->_mulSet.end())
				{
					if (!string2Integer(_pCDBReader->_mulSet[data], eid))
					{
						return false;
					}
				}
				else
				{
					return false;
				}
			}
			break;
		default:
			break;
		}
	}

	tempNodeElement.matNum = eid;
	return true;
}

AnsysAPDLReader_REAL::AnsysAPDLReader_REAL(CDBReader* pCDBReader)
	: ReaderBase(pCDBReader)
{

}

bool AnsysAPDLReader_REAL::Read(std::string& strDataLine)
{
	std::vector<std::string> vecData;
	int n = split(strDataLine, vecData);
	int eid = 0;
	for (int i = 1; i < n; i++)
	{
		std::string data = vecData[i];
		switch (i)
		{
		case 1:
			if (!string2Integer(data, eid))
			{
				if (_pCDBReader->_mulSet.find(data) != _pCDBReader->_mulSet.end())
				{
					if (!string2Integer(_pCDBReader->_mulSet[data], eid))
					{
						return false;
					}
				}
				else
				{
					return false;
				}
			}
			break;
		default:
			break;
		}
	}

	tempNodeElement.realConstantNum = eid;
	return true;
}

AnsysAPDLReader_TSHAPE::AnsysAPDLReader_TSHAPE(CDBReader* pCDBReader)
	: ReaderBase(pCDBReader)
{

}

bool AnsysAPDLReader_TSHAPE::Read(std::string& strDataLine)
{
	std::vector<std::string> vecData;
	int n = split(strDataLine, vecData);
	if (n == 1)
	{ 
		_pCDBReader->AddElement(tempNodeElement);
	}
	else
	{
		int eid = 0;
		for (int i = 1; i < n; i++)
		{
			std::string data = vecData[i];
			toUpper(data);
			switch (i)
			{
			case 1:
				if (data == "PILO"|| data == "POINT")
				{
					tempNodeElement.nodesNumInEle = 1;
				}
				else
				{
					return false;
				}
				break;
			default:
				break;
			}
		}
	}	
	return true;
}

AnsysAPDLReader_EN::AnsysAPDLReader_EN(CDBReader* pCDBReader)
	: ReaderBase(pCDBReader)
{

}

bool AnsysAPDLReader_EN::Read(std::string& strDataLine)
{
	std::vector<std::string> vecData;
	size_t n = split(strDataLine, vecData);
	int eid = 0;
	if (n < 3)
	{
		return false;
	}
	if (!string2Integer(vecData[1], eid))
	{
		return false;
	}
	std::vector<int> nodes(n - 2);
	for (size_t i = 2; i < n; i++)
	{
		std::string data = vecData[i];
		if (!string2Integer(data, nodes[i - 2]))
		{
			if (_pCDBReader->_mulSet.find(data) != _pCDBReader->_mulSet.end())
			{
				if (!string2Integer(_pCDBReader->_mulSet[data], nodes[i - 2]))
				{
					return false;
				}
			}
			else
			{
				return false;
			}
		}
	}

	tempNodeElement.ansysID = eid;
	tempNodeElement.nodeIds = nodes;
	AnsysEN* pEN = new AnsysEN;
	pEN->elementID = eid;
	pEN->nodeID = nodes;
	//_pCDBReader->AddEN(tempNodeElement);
	_pCDBReader->AddElement(tempNodeElement);
	return true;
}
